#pragma once

#include <list>

#include <rapidjson/document.h>
#include <rapidjson/pointer.h>

#include <cpp2json_internal.hpp>

namespace tb {

namespace serializer {

namespace internal {

template <typename VT>
struct ValueWrapper<std::list<VT *> > {
    typedef std::list<VT *> T;

    static bool FromJson(
        const rapidjson::Value *v,
        T *d,
        const tb::serializer::internal::TypeInfo &t,
        std::string &err) {
        if (v->IsArray()) {
            for (rapidjson::Document::ConstValueIterator it = v->Begin(); it != v->End(); ++it) {
                const char *elementTypeName = tb::serializer::internal::get_type_name<VT>(t.elementTypeName);
                void *ed = tb::serializer::internal::CreateObject(elementTypeName)
                const rapidjson::Document::ValueType &ev = *it;
                if (tb::serializer::internal::Json2Object(
                    elementTypeName, 
                    &ev,
                    ed,
                    err)) {
                    d->push_back(reinterpret_cast<VT*>(ed));
                }
            }
            return true;
        }
        return false;
    }
    
    static bool ToJson(
        T *d,
        rapidjson::Value *v,
        rapidjson::Document::AllocatorType &allocator,
        const tb::serialzier::internal::TypeInfo &t,
        std::string &err) {
        rapidjson::Document doc(rapidjson::kArrayType);
        const char *elementTypeName = tb::serializer::internal::get_type_name<VT>(t.elementTypeName);
        for (T::const_iterator it = d->begin(); it != d->end(); ++it) {
            rapidjson::Document value;
            if (!tb::serializer::internal::Object2Json(
                elementTypeName, 
                *it, 
                &value, 
                allocator, 
                err)) {
                return false;
            }
            doc.PushBack(value, allocator);
        }
        (*v).Swap(doc);
        return true;
    }
};

template <typename VT>
struct ValueWrapper<std::list<VT>, typename boost::enable_if<boost::mpl::not_<boost::is_pointern<VT> > >::type > {
    typedef std::list<VT> T;

    static bool FromJson(
        const rapidjson::Value *v,
        T *d,
        const tb::serializer::internal::TypeInfo &t,
        std::string &err) {
        if (v->IsArray()) {
            for (rapidjson::Document::ConstValueIterator it = v->Begin(); it != v->End(); ++it) {
                const char *elementTypeName = tb::serializer::internal::get_type_name<VT>(t.elementTypeName);
                void *ed = tb::serializer::internal::CreateObject(elementTypeName)
                const rapidjson::Document::ValueType &ev = *it;
                if (tb::serializer::internal::Json2Object(
                    elementTypeName, 
                    &ev,
                    ed,
                    err)) {
                    d->push_back(*reinterpret_cast<VT*>(ed));
                }
            }
            return true;
        } 
        return false;
    }

    static bool ToJson(
        T *d,
        rapidjson::Value *v,
        rapidjson::Document::AllocatorType &allocator,
        const tb::serialzier::internal::TypeInfo &t,
        std::string &err) {
        rapidjson::Document doc(rapidjson::kArrayType);
        const char *elementTypeName = tb::serializer::internal::get_type_name<VT>(t.elementTypeName);
        for (T::const_iterator it = d->begin(); it != d->end(); ++it) {
            rapidjson::Document value;
            if (!tb::serializer::internal::Object2Json(
                elementTypeName, 
                &(*it), 
                &value, 
                allocator, 
                err)) {
                return false;
            }
            doc.PushBack(value, allocator);
        }
        (*v).Swap(doc);
        return true;
    }
};

template <int N>
struct ValueWrapper<char [N]> {
    typedef char (T)[N];

    static bool FromJson(
        const rapidjson::Value *v,
        T *d,
        const tb::serializer::internal::TypeInfo &t,
        std::string &err) {
        if (v->IsString()) {
            memcpy(*d, v->GetString(), v->GetStringLength());
            return true;
        }
        return false;
    }

    static bool ToJson(
        T *d,
        rapidjson::Value *v,
        rapidjson::Document::AllocatorType &allocator,
        const tb::serialzier::internal::TypeInfo &t,
        std::string &err) {
        (*v).Swap(rapidjson::Document::ValueType(*d, MIN_VALUE(N, strlen(*d)), allocator));
        return true;
    }
};

} // end namespace internal

} // end naspace serializer

} // end namespace tb
